package com.ruoyi.flow.service;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.flow.model.FlowResult;
import com.ruoyi.flow.model.FlowStartVo;
import com.ruoyi.flow.model.FlowSubmitVo;
import com.ruoyi.flow.model.TodoTask;
import com.youlai.common.exception.BizException;
import com.youlai.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Slf4j
@Service
public class FlowApiServiceImpl implements FlowApiService {

    @Autowired private RuntimeService runtimeService;
    @Autowired private TaskService taskService;
    @Autowired private RepositoryService repositoryService;
    @Autowired private HistoryService historyService;

    @Override
    public FlowResult start(FlowStartVo reqVo) {
        String processKey = String.format("%s_%s", reqVo.getSysCode(), reqVo.getFlowCode());
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(processKey, reqVo.getParams());
        log.info("instId:{}, processDefId:{}, processDefName:{}",
                instance.getId(), instance.getProcessDefinitionId(), instance.getProcessDefinitionName());

        String prodDefName = instance.getProcessDefinitionName();
        // 获取系统和流程名称
        if (StringUtils.isEmpty(prodDefName)) {
            throw new BizException(ResultCode.FAILED.getCode(), "获取流程名称失败");
        }
        String[] names = prodDefName.split("_");
        if (names.length != 2) {
            throw new BizException(ResultCode.FAILED.getCode(), "流程名称格式错误,正确格式:XXX_YYY");
        }

        FlowResult result = new FlowResult();
        result.setInstId(instance.getId());
        result.setSysCode(reqVo.getSysCode());
        result.setSysName(names[0]);
        result.setFlowCode(reqVo.getFlowCode());
        result.setFlowName(names[1]);

        ExecutionEntityImpl executionEntity = (ExecutionEntityImpl) instance;

        String prevNodeCode = executionEntity.getStartActivityId();
        String currNodeCode = executionEntity.getExecutions().get(0).getActivityId();
        String currNodeName = executionEntity.getExecutions().get(0).getActivityName();

        result.setPrevNodeCode(prevNodeCode);
        result.setPrevNodeName("");

        result.setCurrNodeCode(currNodeCode);
        result.setCurrNodeName(currNodeName);

        SequenceFlow sequence = getSequenceFlow(instance.getProcessDefinitionId(), prevNodeCode, currNodeCode);

        result.setLineCode(sequence.getId());
        result.setLineName(sequence.getName());

        // 流转后待办信息
        List<Task> currTasks = taskService.createTaskQuery().processInstanceId(instance.getId()).list();

        // 转化待办信息
        List<TodoTask> todoTasks = convertTodoTask(currTasks);
        result.setTodoTasks(todoTasks);
        return result;
    }

    @Override
    public FlowResult submit(FlowSubmitVo reqVo) {
        TaskQuery query = taskService.createTaskQuery().processInstanceId(reqVo.getInstId()).taskAssignee(reqVo.getLoginName());
        Task prevTask = query.singleResult();
        if (Objects.isNull(prevTask)) {
            log.error("当前用户:{},实例ID:{},不存在待办任务", reqVo.getLoginName(), reqVo.getInstId());
            throw new BizException(ResultCode.FAILED.getCode(), "获取流程名称失败");
        }

        if (!prevTask.getId().equals(reqVo.getCurrNodeCode())) {
            log.error("当前用户{},不存在任务节点<{}>的待办任务", reqVo.getLoginName(), reqVo.getCurrNodeCode());
            throw new BizException(ResultCode.FAILED.getCode(), "当前用户不存在待办任务");
        }
        // 流转前待办信息
        logPreviousTask(prevTask);

        Map<String, String> procDefMap = getProcDefInfo(prevTask.getProcessDefinitionId());

        taskService.complete(prevTask.getId(), reqVo.getParams());

        // 流转后待办信息
        List<Task> currTasks = taskService.createTaskQuery().processInstanceId(reqVo.getInstId()).list();
        logCurrentTask(currTasks, prevTask);

        FlowResult result = new FlowResult();
        result.setInstId(reqVo.getInstId());
        result.setSysCode(procDefMap.get("sysCode"));
        result.setSysName(procDefMap.get("sysName"));
        result.setFlowCode(procDefMap.get("flowCode"));
        result.setFlowName(procDefMap.get("flowName"));

        String prevNodeCode = prevTask.getTaskDefinitionKey();
        String prevNodeName = prevTask.getName();

        // 执行完任务后，获取执行的线
        String currNodeCode = currTasks.get(0).getTaskDefinitionKey();
        String currNodeName = currTasks.get(0).getName();

        SequenceFlow sequenceFlow = getSequenceFlow(prevTask.getProcessDefinitionId(), prevNodeCode, currNodeCode);
        String lineCode = sequenceFlow.getId();
        String lineName = sequenceFlow.getName();

        result.setPrevNodeCode(prevNodeCode);
        result.setPrevNodeName(prevNodeName);
        result.setLineCode(lineCode);
        result.setLineName(lineName);
        result.setCurrNodeCode(currNodeCode);
        result.setCurrNodeName(currNodeName);

        // 转化待办信息
        List<TodoTask> todoTasks = convertTodoTask(currTasks);
        result.setTodoTasks(todoTasks);
        return result;
    }

    private List<TodoTask> convertTodoTask(List<Task> tasks) {
        List<TodoTask> todoTasks = new ArrayList<>();
        if (CollectionUtils.isEmpty(tasks)) {
            return Collections.emptyList();
        }

        String procDefId = tasks.get(0).getProcessDefinitionId();

        Map<String, String> procDefMap = getProcDefInfo(procDefId);

        for (Task task : tasks) {
            TodoTask todoTask = new TodoTask();
            todoTask.setLoginName(task.getAssignee());
            todoTask.setBusiId(""); // TODO
            todoTask.setSysCode(procDefMap.get("sysCode"));
            todoTask.setSysName(procDefMap.get("sysName"));
            todoTask.setFlowCode(procDefMap.get("flowCode"));
            todoTask.setFlowName(procDefMap.get("flowName"));
            todoTask.setNodeCode(task.getTaskDefinitionKey());
            todoTask.setNodeName(task.getName());
            todoTasks.add(todoTask);
        }

        return todoTasks;
    }

    private Map<String, String> getProcDefInfo(String procDefId) {
        String[] items = procDefId.split(":");
        if (items.length != 2) {
            throw new BizException(ResultCode.FAILED.getCode(), "流程定义ID格式错误");
        }
        String[] names = items[0].split("_");
        if (names.length != 2) {
            throw new BizException(ResultCode.FAILED.getCode(), "流程定义ID格式错误");
        }

        String sysCode = names[0];
        String flowCode = names[1];

        String procDefName = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(procDefId).singleResult().getName();

        items = procDefName.split(":");
        if (items.length != 2) {
            throw new BizException(ResultCode.FAILED.getCode(), "流程定义名称格式错误");
        }
        String sysName = items[0];
        String flowName = items[1];

        Map<String, String> map = new HashMap<>();
        map.put("sysCode", names[0]);
        map.put("flowCode", names[1]);
        map.put("sysName", items[0]);
        map.put("flowName", items[1]);

        return map;
    }

    private void logPreviousTask(Task task) {
        log.info("***流转前,当前用户待办信息,流程定义ID:{},流程实例ID", task.getProcessDefinitionId(), task.getProcessInstanceId());
        log.info("   节点编码: {}, 节点名称: {}, 待办人: {}, 任务ID: {}",
                task.getTaskDefinitionKey(), task.getName(), task.getAssignee(), task.getId());
    }

    private void logCurrentTask(List<Task> tasks, Task prevTask) {
        log.info("***流转后,待办信息,流程定义ID:{},流程实例ID:{}", prevTask.getProcessDefinitionId(), prevTask.getProcessInstanceId());
        for (Task task : tasks) {
            log.info("   节点编码: {}, 节点名称: {}, 待办人: {}, 任务ID: {}",
                    task.getTaskDefinitionKey(), task.getName(), task.getAssignee(), task.getId());
        }
    }

    private SequenceFlow getSequenceFlow(String processDefinitionId, String prevActivityId, String currActivityId) {

        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        Collection<SequenceFlow> sequenceFlows = model.getMainProcess().findFlowElementsOfType(SequenceFlow.class);

        SequenceFlow sequenceFlow = null;
        for (SequenceFlow sequence : sequenceFlows) {
            if (((sequence.getSourceRef().equals(prevActivityId)) || sequence.getSourceRef().equals(prevActivityId + "Gateway"))
                    && sequence.getTargetRef().equals(currActivityId)) {
                sequenceFlow = sequence;
                break;
            }
        }

        return sequenceFlow;
    }
}
